package com.aiyan.edu.govern.gateway.route;

import com.aiyan.edu.govern.gateway.event.RefreshRedisRoutesEvent;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.event.EventListener;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;

/**
 * 重写RouteDefinitionRepository，从redis获取路由配置
 * <p>
 * 网关只需关心路由获取即可，这里的save和delete可不实现
 *
 * @author: zqzhou
 * @create: 2019/12/04 10:03
 **/
@Slf4j
@Component
public class RedisRouteDefinitionRepository implements RouteDefinitionRepository, ApplicationEventPublisherAware, ApplicationRunner {

    private static final String GATEWAY_ROUTES = "gateway:routes";

    public List<RouteDefinition> routeDefinitionList = new ArrayList<>();

    ApplicationEventPublisher applicationEventPublisher;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    /**
     * 直接返回内存列表的值
     *
     * @return
     */
    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
        if (null == routeDefinitionList) {
            routeDefinitionList = new ArrayList<>();
        }
        return Flux.fromArray(routeDefinitionList.toArray(new RouteDefinition[routeDefinitionList.size()]));
    }

    /**
     * 监听路由刷新事件 自定义的RefreshRedisRoutesEvent
     * 从Redis获取路由配置并设置到内存列表
     */
    @EventListener
    public void loadRoutesFormRedis(RefreshRedisRoutesEvent event) {
        String gatewayRoutesJson = stringRedisTemplate.opsForValue().get(GATEWAY_ROUTES);
        routeDefinitionList = JSON.parseArray(gatewayRoutesJson, RouteDefinition.class);
        this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
    }

    @Override
    public Mono<Void> save(Mono<RouteDefinition> route) {
        return Mono.empty();
    }

    @Override
    public Mono<Void> delete(Mono<String> routeId) {
        return Mono.empty();
    }

    /**
     * 启动后执行：从redis获取一次路由数据
     *
     * @param args
     * @throws Exception
     */
    @Override
    public void run(ApplicationArguments args) throws Exception {
//        this.applicationEventPublisher.publishEvent(new RefreshRedisRoutesEvent(this));
        loadRoutesFormRedis(null);
    }

    /**
     * 监听消息队列，通知更新路由
     *
     * @param msg
     */
    @RabbitListener(queues = "${aiyanedu.mq.queue}")
    public void publishRefresh(String msg) {
        // TODO: 2019/12/4 解析msg 判断路由版本等
        // 发送RefreshRedisRoutesEvent通知更新路由
//        this.applicationEventPublisher.publishEvent(new RefreshRedisRoutesEvent(this));

        //这里直接调用方法即可
        loadRoutesFormRedis(null);
    }
}
